package com.zysdev.openglbeautydemo.filter

import android.content.Context
import android.opengl.GLES20
import android.util.Log

/**
 * 离屏渲染
 * 将摄像头的数据进行拦截处理，不直接显示在屏幕上
 */
open class BaseFBOFilter(context: Context, vertexShaderId: Int, fragmentShaderId: Int) : BaseFilter(
    context,
    vertexShaderId,
    fragmentShaderId
) {

    //入参出参对象
    private var frameBuffer: IntArray? = null
    private var frameTextures: IntArray? = null

    //初始化
    override fun setSize(width: Int, height: Int) {
        super.setSize(width, height)
//    实例化fbo，让摄像头的数据先渲染到fbo，做了拦截
        releaseFrame()
        //初始化FBO
        frameBuffer = IntArray(1)
        //生成1个fbo，id为入参出参对象，偏移量
        GLES20.glGenFramebuffers(1, frameBuffer, 0)

        //FBO需要绑定一个纹理（图层）
        frameTextures = IntArray(1)
        //让底层传过来一个纹理,放在frameTextures中
        GLES20.glGenTextures(frameTextures!!.size, frameTextures, 0)

        //操作纹理和fbo要，分别进行：1锁定（准备进行原子操作），2操作，3释放

        //对要绑定的纹理进行参数配置
        for (i in frameTextures!!.indices) {
            //原子操作
            //1、锁定要操作的纹理，准备进行参数配置
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextures!![i])

            //2、操作
            //放大过滤是GL_TEXTURE_MAG_FILTER,GL_NEAREST有锯齿，GL_LINEAR无锯齿但模糊
            GLES20.glTexParameteri(
                GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MAG_FILTER,
                GLES20.GL_NEAREST
            )
            //缩小过滤是GL_TEXTURE_MIN_FILTER
            GLES20.glTexParameteri(
                GLES20.GL_TEXTURE_2D,
                GLES20.GL_TEXTURE_MIN_FILTER,
                GLES20.GL_LINEAR
            )
            //3、操作完，解锁释放
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)

        }

        //1、锁定纹理，参数设置完了，准备开始绑定纹理
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, frameTextures!![0])

        /**
         * 指定一个二维的纹理图片
         * level
         * 指定细节级别，0级表示基本图像，n级则表示Mipmap缩小n级之后的图像（缩小2^n）
         * internalformat
         * 指定纹理内部格式，必须是下列符号常量之一：GL_ALPHA，GL_LUMINANCE，GL_LUMINANCE_ALPHA，GL_RGB，GL_RGBA。
         * width height
         * 指定纹理图像的宽高，所有实现都支持宽高至少为64 纹素的2D纹理图像和宽高至少为16 纹素的立方体贴图纹理图像 。
         * border
         * 指定边框的宽度。必须为0。
         * format
         * 指定纹理数据的格式。必须匹配internalformat。下面的符号值被接受：GL_ALPHA，GL_RGB，GL_RGBA，GL_LUMINANCE，和GL_LUMINANCE_ALPHA。
         * type
         * 指定纹理数据的数据类型。下面的符号值被接受：GL_UNSIGNED_BYTE，GL_UNSIGNED_SHORT_5_6_5，GL_UNSIGNED_SHORT_4_4_4_4，和GL_UNSIGNED_SHORT_5_5_5_1。
         * data
         * 指定一个指向内存中图像数据的指针。
         */
        GLES20.glTexImage2D(
            GLES20.GL_TEXTURE_2D,
            0,
            GLES20.GL_RGBA,
            width,
            height,
            0,
            GLES20.GL_RGBA,
            GLES20.GL_UNSIGNED_BYTE,
            null
        )

        //1、锁定要操作的fbo，准备开始使用
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer!![0])

        //2、操作。真正发生fbo和2D纹理进行绑定
        GLES20.glFramebufferTexture2D(
            GLES20.GL_FRAMEBUFFER,
            GLES20.GL_COLOR_ATTACHMENT0,
            GLES20.GL_TEXTURE_2D,
            frameTextures!![0],
            0
        )

        //3、绑定完,解锁释放,成对出现
        GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0)

        //3、操作完fbo,解锁释放，成对出现
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)

    }

    //宽高变化时，释放之前的纹理和fbo，
    private fun releaseFrame() {
        if (frameTextures != null) {
            GLES20.glDeleteTextures(1, frameTextures, 0)
            frameTextures = null
        }
        if (frameBuffer != null) {
            GLES20.glDeleteFramebuffers(1, frameBuffer, 0)
            frameBuffer = null
        }
    }

    //摄像头数据来时，将数据拦截，绘制到fbo的图层中
    override fun onDraw(texture: Int): Int {
        //1、锁定将要操作的纹理图层，（拦截重定向：更改Opengl画面要输出的路径，不更改的话将输出到屏幕，更改后将画面输出到绑定在fbo中的纹理图层中）
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, frameBuffer!![0])
        //2、父类设置通用参数
        super.onDraw(texture)
        //3、解锁释放纹理
        GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0)
        //返回的是一个fbo的纹理图层，可以对此图层进行数据操作，如直播、美颜等等
        return frameTextures!![0]
    }
}